fix(scripts): parseParamNames strips the full TypeScript modifier run, so a parameter property is no longer read as readonly - #13292
Merged
Conversation
`private readonly code: string` parsed as a parameter named `readonly`: the strip was one anchored alternation carrying a `g` flag, and `^` with no `m` matches at position 0 once, so exactly one modifier came off. `helperCodesFor`'s `indexOf(ident)` then answered -1 and the whole code helper was dropped in silence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
Positive cases at every run length TypeScript admits (up to three: accessibility, override, readonly), negative cases for parameters genuinely NAMED after a modifier word, and the end-to-end pair through deriveSites with the single-modifier positive control beside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
This was referenced Aug 29, 2026
os-elon
marked this pull request as ready for review
August 30, 2026 06:16
os-elon
enabled auto-merge
August 30, 2026 06:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13227
parseParamNamesinscripts/check-dispatcher-error-vocabulary.mjscleaned a parameter with a single anchored alternation carrying agflag. The flag reads as "strip them all", but^with nommatches at position 0 once, so exactly one modifier came off:private readonly code: stringparsed as a parameter literally namedreadonly.What that costs — measured, not assumed
helperCodesFordoesparseParamNames(decl.params).indexOf(ident). A wrong name means -1, the identifier is never recognised as a parameter, and the whole helper is dropped: no site AND no unresolved. Same silent-drop class as #9223 / #9460 / #10918 / #13131, one layer further in.Through the real
deriveSites, before (at74049254d4) and after, with the single-modifier positive control beside it so the zeros are readings rather than a dead harness:constructor(private readonly code: string, readonly message: string)sites: []unresolved: []codehelpersite,PARAM_PROP_CODEprivate override readonly code(three modifiers)sites: []THREE_MOD_CODEreadonly code(one modifier)readonlyoverrideThe modifier set, enumerated from the grammar
The card's example is two modifiers. The real maximum is three, and the order is fixed. Measured against this repo's own TypeScript 6.0.3 —
overrideneeds a class that extends another, or the compiler reports it as a class error and the parameter question never gets asked:So the run is
(public|private|protected)? override? readonly?, encoded in that order rather than looped over a set — a "strip any word in this set, repeatedly" loop accepts spellings TypeScript does not, which is the match-everything direction.in/outare TYPE-parameter modifiers and never reach the value-parameter sliceenclosingDeclarationhands over.Why this stayed textual instead of reading the AST
typescriptis a declared root devDependency and two sibling root gates already import it, so the AST route was genuinely available. It is the wrong one here, and the reason is measured rather than inherited from the file's "why textual" header.enclosingDeclaration'sDECL_HEADER_REalso matchesconst x = someCall(. Of the 7646 slices this function is handed onpackages/**non-test source, 1218 are not a valid parameter list at all — they are argument lists — and on 1072 of those a recovering TypeScript parse invents MORE THAN ONE parameter:authService as anybecomes three confident parameters namedauthService,asandany;await res.json()becomesawait,res,json. Those names are exactly whatindexOf(ident)searches. An AST route would therefore MANUFACTURE the wrong-index hazard the card only warns about, across a thousand slices, where the textual reader degrades to one bad name. The over-matching header regex is #13226's subject and is untouched here.Correctness, against a real oracle
Every parameter list the gate reaches on
packages/**non-test source, compared with the TypeScript AST, restricted to slices that really are parameter lists:The 60-list residue is a different defect in
splitTopLevel— the>of=>decrements bracket depth — filed as #13291 and deliberately not fixed here.Blast radius on this tree
The full gate run is byte-identical before and after: 22 unregistered code-stamping sites, all classified, 1 awaiting a ledger entry. 51 declarations are now read correctly, but none of them carries a code stamp assigning one of its parameters, so no verdict moves and no new red is possible. This is prevention, consistent with the p2 grade. The nearest live shape,
MetadataError'sconstructor(public readonly code: string, message: string)inpackages/metadata-core/src/errors.ts, now parses as["code","message"]; all four codes it carries are already registered.Pins
--self-testgrows from 175 to 194 assertions: every run length TypeScript admits, plus a NEGATIVE set for parameters genuinely named after a modifier word —overrideis a real parameter name three times in this repo's own source — plus the end-to-end pair throughderiveSiteswith its control.Reverse-verified from the committed state: restoring only the old one-shot strip while keeping the new pins turns 9 assertions red, naming the exact mis-parses, while every positive control and every other negative case stays green. Mutation confirmed on disk by marker counts and a
git hash-objectchange; restore confirmed by the blob hash matchingHEADwith an emptygit diff HEAD.Local verification, at
ae72953fdfGate family derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(1 changed path), all green:check:agent-test-spelling·check:bash32-floor·check:cli-command-ids·check:cross-package-test-inputs·check:dispatcher-error-vocabulary·check:entry-guard·check:parse-guard·check:pnpm-filter-targets·check:watch-hint-literal·check:nul-bytes·check:pm-dispatch-gates·check-self-test-wired.mjs --self-testand its scan ·check-ci-filter-parity.mjs·check-cross-package-test-inputs.mjs·check-shard-attestation.mjs·pm/bare-root-worklist.mjs --self-test.check-test-completeness.mjsis NOT MEASURED locally: with no argument it exits 3 on its ownPREREQUISITE NOT METbranch, which its failure text states is not a red and not a finding — it grades a savedturbo run testlog that only CI produces.Declarations
packages/specand the runtime doors are untouched.packages/orskills/is modified, so nothing is released.scripts/is CI-internal, which is theskip-changesetcase this repo'slint.ymlnames in prose, so no changeset accompanies this PR.docs/adr/, no.claude/, noskills/, noAGENTS.md, noCLAUDE.md.codehelperis blind to a code helper declared as a CLASS METHOD —enclosingDeclarationhas no header form for one #13226 lands in this same file (enclosingDeclaration/DECL_HEADER_RE) and is held out this round, so onlyparseParamNamesis changed here;DECL_HEADER_RE,enclosingDeclaration,splitTopLevel,SHAPESandOBJECT_LITERAL_CODE_HELPER_BLINDNESSare all byte-unchanged. [finding]codehelperis blind to a code helper declared as a CLASS METHOD —enclosingDeclarationhas no header form for one #13226 and Widencodehelperto the object-literal stamp position — the blast radius is now measured, and 4 undischargeableunresolvedfindings are the blocker #13233 remain open and are not addressed by this branch.Out of scope, filed instead of fixed: #13291.
Generated by Claude Code